﻿#region COPYRIGHT© 2009-2012 Phillip Clark. All rights reserved.
// For licensing information see License.txt (MIT style licensing).
#endregion

using System.Collections.Generic;
using System.Linq;
using FlitBit.Parse;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace FlitBit.Core.Tests
{
	[TestClass]
	public class TokenParserTests
	{
		public enum Orm
		{
			Abstract,
			Extends,
			By,
			Catalog,
			Collection,
			Constrained,
			Discriminator,
			Identifier,
			Identified,
			List,
			Of,
			Property,
			Type,
			Template,
			Uses,
			Version,
			Where,

			CommaSym,
			ColonSym,
			SemicolonSym,
			LParenSym,
			RParenSym,
			LBraceSym,
			RBraceSym,
			LCurlySym,
			RCurlySym,
			DotSym,
			IsEqualSym,

			StringConst,
			NumericConst,
			VersionConst,

			Whitespace,
			ERROR = 0x7FFFFF,
		}

		[TestMethod]
		public void TokenizePropertyDefinition()
		{
			var input = @"
		property Commissioners: collection of Party 
			where Commissioner == Party
				constrained by Party.PartyKind == Person.discriminator
				constrained by Party not in Commissioners;";

			Tokenizer<Orm> parser = new Tokenizer<Orm>();
			parser.IgnoreWhitespace(Orm.Whitespace, TokenSpec.MakeWhitespace("whitespace"))
				.SetIdentifierSpec(Orm.Identifier, TokenSpec.MakeIdentifier("identifier", CharRule.Identifier))
				.AddTokenSpec(Orm.Property, TokenSpec.MakeKeyword("property").IsFollowedBy(TokenFollowedBy.WhitespaceOrSymbol))
				.AddTokenSpec(Orm.Collection, TokenSpec.MakeKeyword("collection").IsFollowedBy(TokenFollowedBy.WhitespaceOrSymbol))
				.AddTokenSpec(Orm.By, TokenSpec.MakeKeyword("by").IsFollowedBy(TokenFollowedBy.WhitespaceOrSymbol))
				.AddTokenSpec(Orm.Of, TokenSpec.MakeKeyword("of").IsFollowedBy(TokenFollowedBy.WhitespaceOrSymbol))
				.AddTokenSpec(Orm.Where, TokenSpec.MakeKeyword("where").IsFollowedBy(TokenFollowedBy.WhitespaceOrSymbol))
				.AddTokenSpec(Orm.Discriminator, TokenSpec.MakeKeyword("discriminator").IsFollowedBy(TokenFollowedBy.WhitespaceOrSymbol))
				.AddTokenSpec(Orm.ColonSym, TokenSpec.MakeSymbol("ColonSym", ':').IsFollowedBy(TokenFollowedBy.NotSymbolOrOperator))
				.AddTokenSpec(Orm.SemicolonSym, TokenSpec.MakeSymbol("SemicolonSym", ';').IsFollowedBy(TokenFollowedBy.NotSymbolOrOperator))
				.AddTokenSpec(Orm.DotSym, TokenSpec.MakeSymbol("DotSym", '.').IsFollowedBy(TokenFollowedBy.NotSymbolOrOperator))
				.AddTokenSpec(Orm.IsEqualSym, TokenSpec.MakeOperator("IsEqualSym", '=', '=').IsFollowedBy(TokenFollowedBy.NotOperator));

			var tokens = new List<Token<Orm>>(from tk in parser.Tokenize(input)
																				select tk);


		}

		[TestMethod]
		public void TokenizeUsesDeclaration()
		{
			var input = @"uses Orm version(0.0.1);";

			Tokenizer<Orm> parser = new Tokenizer<Orm>();
			parser.IgnoreWhitespace(Orm.Whitespace, TokenSpec.MakeWhitespace("whitespace"))
				.SetIdentifierSpec(Orm.Identifier, TokenSpec.MakeIdentifier("identifier", CharRule.Identifier))
				.AddLiteralSpec(Orm.VersionConst, TokenSpec.MakeLiteral("version", Match.MatchExact(CharRule.DecimalDigits
					, CharRule.MatchExact('.')
					, CharRule.DecimalDigits
					, CharRule.MatchExact('.')
					, CharRule.DecimalDigits)))
				.AddTokenSpec(Orm.Uses, TokenSpec.MakeKeyword("uses").IsFollowedBy(TokenFollowedBy.WhitespaceOrSymbol))
				.AddTokenSpec(Orm.Version, TokenSpec.MakeKeyword("version").IsFollowedBy(TokenFollowedBy.WhitespaceOrSymbol))
				.AddTokenSpec(Orm.LParenSym, TokenSpec.MakeSymbol("LParen", '(').IsFollowedBy(TokenFollowedBy.NotOperator))
				.AddTokenSpec(Orm.RParenSym, TokenSpec.MakeSymbol("RParen", ')'))
				.AddTokenSpec(Orm.SemicolonSym, TokenSpec.MakeSymbol("SemicolonSym", ';').IsFollowedBy(TokenFollowedBy.NotSymbolOrOperator))
				.AddTokenSpec(Orm.DotSym, TokenSpec.MakeSymbol("DotSym", '.').IsFollowedBy(TokenFollowedBy.NotSymbolOrOperator))
				;

			var tokens = new List<Token<Orm>>(from tk in parser.Tokenize(input)
																				select tk);

			Assert.AreEqual(Orm.Uses, tokens[0].Kind);
			Assert.AreEqual(Orm.Identifier, tokens[1].Kind);
			Assert.AreEqual(Orm.Version, tokens[2].Kind);
			Assert.AreEqual(Orm.LParenSym, tokens[3].Kind);
			Assert.AreEqual(Orm.VersionConst, tokens[4].Kind);
			Assert.AreEqual(Orm.RParenSym, tokens[5].Kind);
			Assert.AreEqual(Orm.SemicolonSym, tokens[6].Kind);
		}

		[TestMethod]
		public void TokenizeClassDeclaration()
		{
			var input = @"
type template Persistent
	{
		property ID: identity;
				
		identified by ID;
	}";

			Tokenizer<Orm> parser = new Tokenizer<Orm>();
			parser.IgnoreWhitespace(Orm.Whitespace, TokenSpec.MakeWhitespace("whitespace"))
				.SetIdentifierSpec(Orm.Identifier, TokenSpec.MakeIdentifier("identifier", CharRule.Identifier))
				.AddLiteralSpec(Orm.VersionConst, TokenSpec.MakeLiteral("version", Match.MatchExact(CharRule.DecimalDigits
					, CharRule.MatchExact('.')
					, CharRule.DecimalDigits
					, CharRule.MatchExact('.')
					, CharRule.DecimalDigits)))
				.AddTokenSpec(Orm.Type, TokenSpec.MakeKeyword("type").IsFollowedBy(TokenFollowedBy.Whitespace))
				.AddTokenSpec(Orm.Template, TokenSpec.MakeKeyword("template").IsFollowedBy(TokenFollowedBy.Whitespace))
				.AddTokenSpec(Orm.LCurlySym, TokenSpec.MakeSymbol("LCurly", '{').IsFollowedBy(TokenFollowedBy.NotSymbolOrOperator))
				.AddTokenSpec(Orm.RCurlySym, TokenSpec.MakeSymbol("RCurly", '}').IsFollowedBy(TokenFollowedBy.NotSymbolOrOperator))
				.AddTokenSpec(Orm.Property, TokenSpec.MakeKeyword("property").IsFollowedBy(TokenFollowedBy.Whitespace))
				.AddTokenSpec(Orm.ColonSym, TokenSpec.MakeSymbol("Colon", ':').IsFollowedBy(TokenFollowedBy.NotSymbolOrOperator))
				.AddTokenSpec(Orm.SemicolonSym, TokenSpec.MakeSymbol("Semicolon", ';').IsFollowedBy(TokenFollowedBy.NotOperator))
				.AddTokenSpec(Orm.Identified, TokenSpec.MakeKeyword("identified").IsFollowedBy(TokenFollowedBy.Whitespace))
				.AddTokenSpec(Orm.By, TokenSpec.MakeKeyword("by").IsFollowedBy(TokenFollowedBy.Whitespace))
				;

			var tokens = new List<string>(from tk in parser.Tokenize(input)
																		select tk.Capture.Value);

			Assert.IsNotNull(tokens);

		}


		[TestMethod]
		public void TokenizeAbstractClass()
		{
			var input = @"
abstract type Party { property PartyKind: descriminator; property Name: string(80); identified by PartyKind, Name; }";

			Tokenizer<Orm> parser = new Tokenizer<Orm>();
			parser.IgnoreWhitespace(Orm.Whitespace, TokenSpec.MakeWhitespace("whitespace"))
				.SetIdentifierSpec(Orm.Identifier, TokenSpec.MakeIdentifier("identifier", CharRule.Identifier))
				.AddLiteralSpec(Orm.NumericConst, TokenSpec.MakeLiteral("digits", CharRule.DecimalDigits))
				.AddLiteralSpec(Orm.VersionConst, TokenSpec.MakeLiteral("version", Match.MatchExact(CharRule.DecimalDigits
					, CharRule.MatchExact('.')
					, CharRule.DecimalDigits
					, CharRule.MatchExact('.')
					, CharRule.DecimalDigits)))
				.AddTokenSpec(Orm.Abstract, TokenSpec.MakeKeyword("abstract").IsFollowedBy(TokenFollowedBy.Whitespace))
				.AddTokenSpec(Orm.Type, TokenSpec.MakeKeyword("type").IsFollowedBy(TokenFollowedBy.Whitespace))
				.AddTokenSpec(Orm.Discriminator, TokenSpec.MakeKeyword("discriminator").IsFollowedBy(TokenFollowedBy.Whitespace))
				.AddTokenSpec(Orm.LCurlySym, TokenSpec.MakeSymbol("LCurly", '{').IsFollowedBy(TokenFollowedBy.NotSymbolOrOperator))
				.AddTokenSpec(Orm.RCurlySym, TokenSpec.MakeSymbol("RCurly", '}').IsFollowedBy(TokenFollowedBy.NotSymbolOrOperator))
				.AddTokenSpec(Orm.LParenSym, TokenSpec.MakeSymbol("LParen", '(').IsFollowedBy(TokenFollowedBy.NotOperator))
				.AddTokenSpec(Orm.RParenSym, TokenSpec.MakeSymbol("RParen", ')'))
				.AddTokenSpec(Orm.Property, TokenSpec.MakeKeyword("property").IsFollowedBy(TokenFollowedBy.Whitespace))
				.AddTokenSpec(Orm.ColonSym, TokenSpec.MakeSymbol("Colon", ':').IsFollowedBy(TokenFollowedBy.NotSymbolOrOperator))
				.AddTokenSpec(Orm.SemicolonSym, TokenSpec.MakeSymbol("Semicolon", ';').IsFollowedBy(TokenFollowedBy.NotOperator))
				.AddTokenSpec(Orm.CommaSym, TokenSpec.MakeSymbol("Comma", ',').IsFollowedBy(TokenFollowedBy.NotOperator))
				.AddTokenSpec(Orm.Identified, TokenSpec.MakeKeyword("identified").IsFollowedBy(TokenFollowedBy.Whitespace))
				.AddTokenSpec(Orm.By, TokenSpec.MakeKeyword("by").IsFollowedBy(TokenFollowedBy.Whitespace))
				;

			var tokens = new List<string>(from tk in parser.Tokenize(input)
																		select tk.Capture.Value);
			Assert.IsNotNull(tokens);
		}
	}
}
